import numpy as np
import pandas as pd
from datetime import datetime, timedelta
from django.utils import timezone
from django.db import transaction
from typing import Dict, List, Optional, Tuple
from .models import RiskIndicator, RiskAlert, RiskAssessmentResult, RiskLimit, RiskMonitoringTask
from portfolios.models import Portfolio, PortfolioAsset
from products.models import Product
from data_service.models import MarketData


class RiskCalculationService:
    """
    风险计算服务
    """
    
    def __init__(self):
        self.confidence_levels = [0.95, 0.99]
        self.time_horizons = [1, 5, 10, 22]  # 天数
    
    def calculate_portfolio_risk(self, portfolio_id: int) -> RiskAssessmentResult:
        """
        计算投资组合风险
        """
        portfolio = Portfolio.objects.get(id=portfolio_id)
        
        # 获取投资组合资产
        portfolio_assets = PortfolioAsset.objects.filter(portfolio=portfolio)
        
        if not portfolio_assets.exists():
            raise ValueError("投资组合没有资产")
        
        # 计算各种风险指标
        risk_metrics = self._calculate_risk_metrics(portfolio_assets)
        
        # 计算风险评分
        risk_score = self._calculate_risk_score(risk_metrics)
        
        # 确定风险等级
        risk_level = self._determine_risk_level(risk_score)
        
        # 保存评估结果
        assessment_result = RiskAssessmentResult.objects.create(
            portfolio=portfolio,
            risk_score=risk_score,
            risk_level=risk_level,
            risk_metrics=risk_metrics,
            assessment_date=timezone.now()
        )
        
        # 检查风险预警
        self._check_risk_alerts(portfolio, risk_metrics)
        
        return assessment_result
    
    def _calculate_risk_metrics(self, portfolio_assets) -> Dict:
        """
        计算风险指标
        """
        # 获取历史价格数据
        price_data = self._get_historical_prices(portfolio_assets)
        
        if price_data.empty:
            return {}
        
        # 计算收益率
        returns = price_data.pct_change().dropna()
        
        # 计算投资组合权重
        weights = self._calculate_portfolio_weights(portfolio_assets)
        
        # 计算投资组合收益率
        portfolio_returns = (returns * weights).sum(axis=1)
        
        metrics = {}
        
        # 计算VaR (Value at Risk)
        for confidence in self.confidence_levels:
            var = np.percentile(portfolio_returns, (1 - confidence) * 100)
            metrics[f'var_{int(confidence*100)}'] = float(var)
        
        # 计算CVaR (Conditional Value at Risk)
        for confidence in self.confidence_levels:
            var = np.percentile(portfolio_returns, (1 - confidence) * 100)
            cvar = portfolio_returns[portfolio_returns <= var].mean()
            metrics[f'cvar_{int(confidence*100)}'] = float(cvar)
        
        # 计算波动率
        volatility = portfolio_returns.std() * np.sqrt(252)  # 年化波动率
        metrics['volatility'] = float(volatility)
        
        # 计算夏普比率
        risk_free_rate = 0.03  # 假设无风险利率为3%
        excess_returns = portfolio_returns.mean() * 252 - risk_free_rate
        sharpe_ratio = excess_returns / volatility if volatility > 0 else 0
        metrics['sharpe_ratio'] = float(sharpe_ratio)
        
        # 计算最大回撤
        cumulative_returns = (1 + portfolio_returns).cumprod()
        rolling_max = cumulative_returns.expanding().max()
        drawdown = (cumulative_returns - rolling_max) / rolling_max
        max_drawdown = drawdown.min()
        metrics['max_drawdown'] = float(max_drawdown)
        
        # 计算贝塔系数（相对于市场指数）
        market_returns = self._get_market_returns()
        if not market_returns.empty:
            covariance = np.cov(portfolio_returns, market_returns)[0][1]
            market_variance = np.var(market_returns)
            beta = covariance / market_variance if market_variance > 0 else 1
            metrics['beta'] = float(beta)
        
        # 计算相关性矩阵
        correlation_matrix = returns.corr()
        metrics['correlation_matrix'] = correlation_matrix.to_dict()
        
        return metrics
    
    def _get_historical_prices(self, portfolio_assets) -> pd.DataFrame:
        """
        获取历史价格数据
        """
        end_date = timezone.now().date()
        start_date = end_date - timedelta(days=365)  # 获取一年的数据
        
        price_data = pd.DataFrame()
        
        for asset in portfolio_assets:
            product = asset.product
            
            # 从MarketData获取历史价格
            market_data = MarketData.objects.filter(
                symbol=product.code,
                date__range=[start_date, end_date]
            ).order_by('date')
            
            if market_data.exists():
                prices = pd.Series(
                    [data.close_price for data in market_data],
                    index=[data.date for data in market_data],
                    name=product.code
                )
                price_data[product.code] = prices
        
        return price_data
    
    def _calculate_portfolio_weights(self, portfolio_assets) -> pd.Series:
        """
        计算投资组合权重
        """
        total_value = sum(asset.quantity * asset.average_cost for asset in portfolio_assets)
        
        weights = {}
        for asset in portfolio_assets:
            asset_value = asset.quantity * asset.average_cost
            weights[asset.product.code] = asset_value / total_value if total_value > 0 else 0
        
        return pd.Series(weights)
    
    def _get_market_returns(self) -> pd.Series:
        """
        获取市场收益率（使用市场指数）
        """
        end_date = timezone.now().date()
        start_date = end_date - timedelta(days=365)
        
        # 假设使用沪深300指数作为市场基准
        market_data = MarketData.objects.filter(
            symbol='000300',  # 沪深300指数代码
            date__range=[start_date, end_date]
        ).order_by('date')
        
        if market_data.exists():
            prices = pd.Series(
                [data.close_price for data in market_data],
                index=[data.date for data in market_data]
            )
            return prices.pct_change().dropna()
        
        return pd.Series()
    
    def _calculate_risk_score(self, risk_metrics: Dict) -> float:
        """
        计算综合风险评分 (0-100)
        """
        if not risk_metrics:
            return 50.0  # 默认中等风险
        
        score = 0
        weight_sum = 0
        
        # VaR权重: 30%
        if 'var_95' in risk_metrics:
            var_score = min(abs(risk_metrics['var_95']) * 1000, 100)
            score += var_score * 0.3
            weight_sum += 0.3
        
        # 波动率权重: 25%
        if 'volatility' in risk_metrics:
            vol_score = min(risk_metrics['volatility'] * 100, 100)
            score += vol_score * 0.25
            weight_sum += 0.25
        
        # 最大回撤权重: 25%
        if 'max_drawdown' in risk_metrics:
            dd_score = min(abs(risk_metrics['max_drawdown']) * 100, 100)
            score += dd_score * 0.25
            weight_sum += 0.25
        
        # 夏普比率权重: 20% (负向评分，夏普比率越高风险评分越低)
        if 'sharpe_ratio' in risk_metrics:
            sharpe_score = max(0, 50 - risk_metrics['sharpe_ratio'] * 10)
            score += sharpe_score * 0.2
            weight_sum += 0.2
        
        return score / weight_sum if weight_sum > 0 else 50.0
    
    def _determine_risk_level(self, risk_score: float) -> str:
        """
        确定风险等级
        """
        if risk_score <= 20:
            return 'low'
        elif risk_score <= 40:
            return 'medium_low'
        elif risk_score <= 60:
            return 'medium'
        elif risk_score <= 80:
            return 'medium_high'
        else:
            return 'high'
    
    def _check_risk_alerts(self, portfolio: Portfolio, risk_metrics: Dict):
        """
        检查风险预警
        """
        # 获取活跃的风险指标
        risk_indicators = RiskIndicator.objects.filter(is_active=True)
        
        for indicator in risk_indicators:
            metric_value = risk_metrics.get(indicator.metric_name)
            
            if metric_value is not None:
                # 检查是否超过阈值
                if self._is_threshold_breached(metric_value, indicator.threshold_value, indicator.comparison_operator):
                    # 创建风险预警
                    self._create_risk_alert(portfolio, indicator, metric_value)
    
    def _is_threshold_breached(self, current_value: float, threshold: float, operator: str) -> bool:
        """
        检查是否突破阈值
        """
        if operator == 'gt':
            return current_value > threshold
        elif operator == 'gte':
            return current_value >= threshold
        elif operator == 'lt':
            return current_value < threshold
        elif operator == 'lte':
            return current_value <= threshold
        elif operator == 'eq':
            return abs(current_value - threshold) < 0.0001
        return False
    
    def _create_risk_alert(self, portfolio: Portfolio, indicator: RiskIndicator, current_value: float):
        """
        创建风险预警
        """
        # 检查是否已存在相同的活跃预警
        existing_alert = RiskAlert.objects.filter(
            portfolio=portfolio,
            indicator=indicator,
            status='active'
        ).first()
        
        if not existing_alert:
            # 确定预警级别
            alert_level = self._determine_alert_level(current_value, indicator.threshold_value)
            
            RiskAlert.objects.create(
                portfolio=portfolio,
                indicator=indicator,
                alert_level=alert_level,
                current_value=current_value,
                threshold_value=indicator.threshold_value,
                message=f"{indicator.name}超过阈值: 当前值{current_value:.4f}, 阈值{indicator.threshold_value:.4f}",
                status='active'
            )
    
    def _determine_alert_level(self, current_value: float, threshold_value: float) -> str:
        """
        确定预警级别
        """
        ratio = abs(current_value - threshold_value) / abs(threshold_value)
        
        if ratio >= 0.5:
            return 'critical'
        elif ratio >= 0.3:
            return 'high'
        elif ratio >= 0.1:
            return 'medium'
        else:
            return 'low'


class RiskMonitoringService:
    """
    风险监控服务
    """
    
    def __init__(self):
        self.risk_calculation_service = RiskCalculationService()
    
    def execute_task(self, task: RiskMonitoringTask) -> Dict:
        """
        执行监控任务
        """
        task.status = 'running'
        task.started_at = timezone.now()
        task.save()
        
        try:
            result = {}
            
            if task.task_type == 'risk_assessment':
                result = self._execute_risk_assessment_task(task)
            elif task.task_type == 'limit_monitoring':
                result = self._execute_limit_monitoring_task(task)
            elif task.task_type == 'alert_check':
                result = self._execute_alert_check_task(task)
            elif task.task_type == 'portfolio_analysis':
                result = self._execute_portfolio_analysis_task(task)
            
            task.status = 'completed'
            task.completed_at = timezone.now()
            task.result = result
            task.save()
            
            return result
            
        except Exception as e:
            task.status = 'failed'
            task.error_message = str(e)
            task.completed_at = timezone.now()
            task.save()
            raise
    
    def _execute_risk_assessment_task(self, task: RiskMonitoringTask) -> Dict:
        """
        执行风险评估任务
        """
        if task.portfolio:
            # 单个投资组合评估
            assessment = self.risk_calculation_service.calculate_portfolio_risk(task.portfolio.id)
            return {
                'portfolio_id': task.portfolio.id,
                'risk_score': assessment.risk_score,
                'risk_level': assessment.risk_level,
                'assessment_id': assessment.id
            }
        else:
            # 所有投资组合评估
            portfolios = Portfolio.objects.filter(is_active=True)
            results = []
            
            for portfolio in portfolios:
                try:
                    assessment = self.risk_calculation_service.calculate_portfolio_risk(portfolio.id)
                    results.append({
                        'portfolio_id': portfolio.id,
                        'risk_score': assessment.risk_score,
                        'risk_level': assessment.risk_level,
                        'assessment_id': assessment.id
                    })
                except Exception as e:
                    results.append({
                        'portfolio_id': portfolio.id,
                        'error': str(e)
                    })
            
            return {
                'total_portfolios': len(portfolios),
                'successful_assessments': len([r for r in results if 'error' not in r]),
                'failed_assessments': len([r for r in results if 'error' in r]),
                'results': results
            }
    
    def _execute_limit_monitoring_task(self, task: RiskMonitoringTask) -> Dict:
        """
        执行限额监控任务
        """
        if task.portfolio:
            limits = RiskLimit.objects.filter(portfolio=task.portfolio, is_active=True)
        else:
            limits = RiskLimit.objects.filter(is_active=True)
        
        breached_limits = []
        
        for limit in limits:
            limit.update_utilization()
            if limit.is_breached:
                breached_limits.append({
                    'limit_id': limit.id,
                    'portfolio_id': limit.portfolio.id,
                    'limit_type': limit.limit_type,
                    'current_value': limit.current_value,
                    'limit_value': limit.limit_value,
                    'utilization': (limit.current_value / limit.limit_value) * 100
                })
        
        return {
            'total_limits': limits.count(),
            'breached_limits': len(breached_limits),
            'breached_details': breached_limits
        }
    
    def _execute_alert_check_task(self, task: RiskMonitoringTask) -> Dict:
        """
        执行预警检查任务
        """
        if task.portfolio:
            portfolios = [task.portfolio]
        else:
            portfolios = Portfolio.objects.filter(is_active=True)
        
        new_alerts = []
        
        for portfolio in portfolios:
            try:
                # 重新计算风险指标
                portfolio_assets = PortfolioAsset.objects.filter(portfolio=portfolio)
                if portfolio_assets.exists():
                    risk_metrics = self.risk_calculation_service._calculate_risk_metrics(portfolio_assets)
                    
                    # 检查风险预警
                    alerts_before = RiskAlert.objects.filter(portfolio=portfolio, status='active').count()
                    self.risk_calculation_service._check_risk_alerts(portfolio, risk_metrics)
                    alerts_after = RiskAlert.objects.filter(portfolio=portfolio, status='active').count()
                    
                    if alerts_after > alerts_before:
                        new_alerts.append({
                            'portfolio_id': portfolio.id,
                            'new_alerts_count': alerts_after - alerts_before
                        })
            except Exception as e:
                new_alerts.append({
                    'portfolio_id': portfolio.id,
                    'error': str(e)
                })
        
        return {
            'checked_portfolios': len(portfolios),
            'portfolios_with_new_alerts': len([a for a in new_alerts if 'error' not in a]),
            'new_alerts': new_alerts
        }
    
    def _execute_portfolio_analysis_task(self, task: RiskMonitoringTask) -> Dict:
        """
        执行投资组合分析任务
        """
        if task.portfolio:
            portfolios = [task.portfolio]
        else:
            portfolios = Portfolio.objects.filter(is_active=True)
        
        analysis_results = []
        
        for portfolio in portfolios:
            try:
                # 获取最新的风险评估结果
                latest_assessment = RiskAssessmentResult.objects.filter(
                    portfolio=portfolio
                ).order_by('-assessment_date').first()
                
                # 统计预警和限额情况
                active_alerts = RiskAlert.objects.filter(
                    portfolio=portfolio, 
                    status='active'
                ).count()
                
                breached_limits = RiskLimit.objects.filter(
                    portfolio=portfolio, 
                    is_breached=True
                ).count()
                
                analysis_results.append({
                    'portfolio_id': portfolio.id,
                    'portfolio_name': portfolio.name,
                    'risk_score': latest_assessment.risk_score if latest_assessment else None,
                    'risk_level': latest_assessment.risk_level if latest_assessment else None,
                    'active_alerts': active_alerts,
                    'breached_limits': breached_limits,
                    'last_assessment': latest_assessment.assessment_date.isoformat() if latest_assessment else None
                })
                
            except Exception as e:
                analysis_results.append({
                    'portfolio_id': portfolio.id,
                    'error': str(e)
                })
        
        return {
            'analyzed_portfolios': len(portfolios),
            'analysis_results': analysis_results
        }
    
    def create_scheduled_tasks(self):
        """
        创建定时任务
        """
        # 每日风险评估任务
        RiskMonitoringTask.objects.get_or_create(
            task_type='risk_assessment',
            portfolio=None,
            defaults={
                'task_name': '每日风险评估',
                'description': '对所有活跃投资组合进行风险评估',
                'schedule_time': timezone.now() + timedelta(hours=1),
                'is_recurring': True,
                'recurrence_pattern': 'daily',
                'status': 'pending'
            }
        )
        
        # 每小时限额监控任务
        RiskMonitoringTask.objects.get_or_create(
            task_type='limit_monitoring',
            portfolio=None,
            defaults={
                'task_name': '限额监控',
                'description': '监控所有风险限额的使用情况',
                'schedule_time': timezone.now() + timedelta(minutes=30),
                'is_recurring': True,
                'recurrence_pattern': 'hourly',
                'status': 'pending'
            }
        )
        
        # 每15分钟预警检查任务
        RiskMonitoringTask.objects.get_or_create(
            task_type='alert_check',
            portfolio=None,
            defaults={
                'task_name': '预警检查',
                'description': '检查所有投资组合的风险预警',
                'schedule_time': timezone.now() + timedelta(minutes=15),
                'is_recurring': True,
                'recurrence_pattern': 'every_15_minutes',
                'status': 'pending'
            }
        )